#include "WATCardOffice.h"
#include <iostream>

/***********************************************************************/
WATCardOffice::Courier::Courier()
{
}

WATCardOffice::Courier::~Courier()
{
	while(myJobmine.size() != 0)
	{
		Job* one_active_application = myJobmine.front();
		myJobmine.pop_front();

		delete one_active_application;
	}
}

void WATCardOffice::Courier::set(unsigned int id, WATCardOffice *CardOffice, Bank *bank, Printer * printer)
{
	myID = id;
    myCardOffice = CardOffice;
    myBank = bank;
    myPrinter = printer;
}

void WATCardOffice::Courier::main()
{
	// make sure that set is called before we start using any 
	// pointers to prevent null pointer exception.
	_Accept(set);

    myPrinter->print(Printer::Courier, myID, 'S');
    for(;;)
    {
        Job * job = myCardOffice->requestWork();
        if(job == NULL) break;
        myPrinter->print(Printer::Courier, myID, 't',
                            job->args.myID, job->args.myAmount );
        myBank->withdraw(job->args.myID, job->args.myAmount);
        job->args.realCard->deposit(job->args.myAmount);
        myPrinter->print(Printer::Courier, myID, 'T',
                            job->args.myID, job->args.myAmount );
        int lost = Global::prng(0,5);
        if(lost == 0)   // WATCard was lost!
        {
            Lost* lost = new Lost();
            job->result.exception(lost);
        }else
        {
            job->result.delivery(job->args.realCard);
        }

		myJobmine.push_back(job);
    }
    myPrinter->print(Printer::Courier, myID, 'F');
}

/***********************************************************************/
WATCardOffice::WATCardOffice( Printer &prt, Bank &bank, 
							  unsigned int numCouriers ) :
myPrinter(prt), myBank(bank)
{
	myNumCouriers = numCouriers;
	myCouriers = new Courier[numCouriers]();
	for(unsigned int i=0; i<numCouriers; i++)
	{
	    myCouriers[i].set(i, this, &myBank, &myPrinter);
	}
}

WATCardOffice::~WATCardOffice()
{
    // destructor called when all students are satisfied
    // which guarantees there is no outstanding work
    delete[] myCouriers;
}

void WATCardOffice::main()
{
    myPrinter.print(Printer::WATCardOffice, 'S');
    for(;;)
    {
        _Accept(~WATCardOffice)
        {
            break;
        }or _When(!requests.empty()) _Accept(requestWork)
        {
            myPrinter.print(Printer::WATCardOffice, 'W');
        }or _Accept(create)
        {
            myPrinter.print(Printer::WATCardOffice, 'C');
        }or _Accept(transfer)
        {
            myPrinter.print(Printer::WATCardOffice, 'T');
        }
        
    }
    
    // now we must send signals to the couriers 
    // so that they can end their main()
    // currently the requests queue is empty
    // so all the couriers are blocked in requestWork
    // we send NULL jobs to couriers to tell them
    // it's time to rest
    for(unsigned int i=0; i<myNumCouriers;i++)
    {
        requests.push_back(NULL);
        _Accept(requestWork);
    }
    myPrinter.print(Printer::WATCardOffice, 'F');
    
}

FWATCard WATCardOffice::create ( unsigned int sid, unsigned int amount, WATCard *&card )
{
    card = new WATCard();
    Args args(card, amount, sid);
    Job * job = new Job(args);
    requests.push_back(job);
	return job->result;
}

FWATCard WATCardOffice::transfer( unsigned int sid, unsigned int amount, WATCard *card)
{
    Args args(card, amount, sid);
    Job * job = new Job(args);
    requests.push_back(job);
	return job->result;
}

WATCardOffice::Job *WATCardOffice::requestWork()
{
    Job * job = requests.front();
    requests.pop_front();
    return job;
}

